package org.eclipse.soc.ide4edu.assignment.wizards;

import java.io.ByteArrayInputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.InputStream;

import org.eclipse.core.runtime.IStatus;
import org.eclipse.core.runtime.Status;
import org.eclipse.jface.dialogs.ErrorDialog;
import org.eclipse.jface.viewers.ISelection;
import org.eclipse.jface.wizard.WizardPage;
import org.eclipse.soc.ide4edu.assignment.editors.BufferStore;
import org.eclipse.soc.ide4edu.assignment.editors.EditorDialog;
import org.eclipse.swt.SWT;
import org.eclipse.swt.custom.StyledText;
import org.eclipse.swt.events.ModifyEvent;
import org.eclipse.swt.events.ModifyListener;
import org.eclipse.swt.events.SelectionAdapter;
import org.eclipse.swt.events.SelectionEvent;
import org.eclipse.swt.layout.GridData;
import org.eclipse.swt.layout.GridLayout;
import org.eclipse.swt.widgets.Button;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.FileDialog;
import org.eclipse.swt.widgets.Group;
import org.eclipse.swt.widgets.Label;

public class AssignmentDescriptionWizardPage extends WizardPage {

	/*--- SWT Widgets ---*/
	private StyledText descriptionImportPathText;
	private StyledText evaluationImportPathText;
	private StyledText descriptionTextField;
	private StyledText evaluationTextField;

	private Button loadExternalDescriptionFile;
	private Button loadExternalEvaluationFile;
	private Button descriptionBrowse;
	private Button evaluationBrowse;
	private Button editDesc;
	private Button editEval;
	/*--- End ---*/

	// Instances of BufferStore to render Rich Text
	private BufferStore descriptionBuffer = null;
	private BufferStore evaluationBuffer = null;

	/**
	 * Constructor of this class.
	 * 
	 * @param select
	 *            ISelection element
	 */
	public AssignmentDescriptionWizardPage(ISelection select) {
		super("TeacherWizardPage2");
		setTitle("Assignment Description Wizard Page");

		descriptionBuffer = new BufferStore();
		evaluationBuffer = new BufferStore();
		setMessage("Please Click on 'Edit' to edit your text");
	}

	@Override
	public void createControl(Composite parent) {
		Composite composite = new Composite(parent, SWT.NONE);
		GridLayout gl = new GridLayout();
		composite.setLayout(gl);

		gl = new GridLayout();
		GridData gd = new GridData(GridData.FILL_HORIZONTAL);
		gl.numColumns = 3;

		Group descriptionGroup = new Group(composite, SWT.SHADOW_IN);
		descriptionGroup.setText("Functional Requirements");
		descriptionGroup.setLayout(gl);
		descriptionGroup.setLayoutData(gd);

		gd = new GridData(GridData.FILL_BOTH);
		gd.grabExcessHorizontalSpace = true;
		gd.widthHint = 300;
		gd.heightHint = 140;
		gd.horizontalSpan = 3;

		descriptionTextField = new StyledText(descriptionGroup, SWT.BORDER
				| SWT.MULTI | SWT.WRAP | SWT.V_SCROLL | SWT.READ_ONLY);
		descriptionTextField.setTextLimit(5000);
		descriptionTextField.setEnabled(false);
		descriptionTextField.setLayoutData(gd);

		editDesc = new Button(descriptionGroup, SWT.PUSH);
		editDesc.setText("Edit");
		editDesc.addSelectionListener(new SelectionAdapter() {
			@Override
			public void widgetSelected(SelectionEvent e) {
				openEditorDialog(e);
			}
		});

		gd = new GridData();
		gd.horizontalSpan = 3;
		editDesc.setLayoutData(gd);
		gd = new GridData(GridData.FILL_HORIZONTAL);
		gd.grabExcessHorizontalSpace = true;
		gd.horizontalSpan = 3;
		loadExternalDescriptionFile = new Button(descriptionGroup, SWT.CHECK);
		loadExternalDescriptionFile.setText("Load asg File: ");
		loadExternalDescriptionFile.setLayoutData(gd);

		gd = new GridData(GridData.FILL_HORIZONTAL);
		gd.grabExcessHorizontalSpace = true;
		gd.widthHint = 400;
		new Label(descriptionGroup, SWT.BEGINNING).setText("Path: ");
		descriptionImportPathText = new StyledText(descriptionGroup, SWT.BORDER
				| SWT.SINGLE);
		descriptionImportPathText.setEnabled(false);
		descriptionImportPathText.addModifyListener(new ModifyListener() {
			public void modifyText(ModifyEvent e) {
				dialogChanged(e);
			}
		});
		descriptionBrowse = new Button(descriptionGroup, SWT.PUSH);
		descriptionBrowse.setText("Browse..");
		descriptionBrowse.addSelectionListener(new SelectionAdapter() {
			@Override
			public void widgetSelected(SelectionEvent e) {
				handleButtons(e);
			}
		});
		descriptionImportPathText.setLayoutData(gd);

		gl = new GridLayout();
		gd = new GridData(GridData.FILL_HORIZONTAL);
		gl.numColumns = 3;

		Group evaluationCriteriaGroup = new Group(composite, SWT.SHADOW_IN);
		evaluationCriteriaGroup.setText("Evaluation Criteria");
		evaluationCriteriaGroup.setLayout(gl);
		evaluationCriteriaGroup.setLayoutData(gd);

		gd = new GridData(GridData.FILL_BOTH);
		gd.grabExcessHorizontalSpace = true;
		gd.widthHint = 300;
		gd.heightHint = 140;
		gd.horizontalSpan = 3;
		evaluationTextField = new StyledText(evaluationCriteriaGroup,
				SWT.BORDER | SWT.MULTI | SWT.WRAP | SWT.V_SCROLL
						| SWT.READ_ONLY);
		evaluationTextField.setTextLimit(5000);
		evaluationTextField.setEnabled(false);
		evaluationTextField.setLayoutData(gd);

		editEval = new Button(evaluationCriteriaGroup, SWT.PUSH);
		editEval.setText("Edit");
		editEval.addSelectionListener(new SelectionAdapter() {
			@Override
			public void widgetSelected(SelectionEvent e) {
				openEditorDialog(e);
			}
		});

		gd = new GridData();
		gd.horizontalSpan = 3;
		editEval.setLayoutData(gd);

		gd = new GridData(GridData.FILL_HORIZONTAL);
		gd.grabExcessHorizontalSpace = true;
		gd.horizontalSpan = 3;
		loadExternalEvaluationFile = new Button(evaluationCriteriaGroup,
				SWT.CHECK);
		loadExternalEvaluationFile.setText("Load asg File: ");
		loadExternalEvaluationFile.setLayoutData(gd);

		gd = new GridData(GridData.FILL_HORIZONTAL);
		gd.grabExcessHorizontalSpace = true;
		gd.widthHint = 400;
		new Label(evaluationCriteriaGroup, SWT.BEGINNING).setText("Path: ");
		evaluationImportPathText = new StyledText(evaluationCriteriaGroup,
				SWT.BORDER | SWT.SINGLE);
		evaluationImportPathText.setEnabled(false);
		evaluationImportPathText.addModifyListener(new ModifyListener() {
			public void modifyText(ModifyEvent e) {
				dialogChanged(e);
			}
		});

		evaluationBrowse = new Button(evaluationCriteriaGroup, SWT.PUSH);
		evaluationBrowse.setText("Browse..");
		evaluationBrowse.addSelectionListener(new SelectionAdapter() {
			@Override
			public void widgetSelected(SelectionEvent e) {
				handleButtons(e);
			}
		});
		evaluationImportPathText.setLayoutData(gd);

		setControl(composite);

	}

	// Handles Text Change Event in the Description and Evaluation Import Fields
	private void dialogChanged(ModifyEvent event) {

		if (event.widget == descriptionImportPathText) {
			final String path = getdescriptionImportPath();
			readDescriptionFromFile(path);
		}

		if (event.widget == evaluationImportPathText) {
			final String path = getEvaluationImportPath();
			readEvaluationFromFile(path);
		}

	}

	/**
	 * Reads the Assignment's Description from the descriptionBuffer
	 * 
	 * @return an InputStream of the Assignment's Description
	 */
	public InputStream getAssignmentDescription() {
		return new ByteArrayInputStream(descriptionBuffer.getBuffer()
				.getBytes());
	}

	/**
	 * Reads the Assignment's Evaluation from the evaluationBuffer
	 * 
	 * @return an InputStream of the Assignment's Evaluation
	 */
	public InputStream getAssignmentEvaluation() {
		return new ByteArrayInputStream(evaluationBuffer.getBuffer().getBytes());
	}

	/**
	 * Helper method to Get the Description's Import Path. Also takes care of
	 * the Path separators of Windows.
	 * 
	 * @return the String representation of the Assignment Description Import
	 *         Path.
	 */
	protected final String getdescriptionImportPath() {
		if (isWindows()) {
			String path = descriptionImportPathText.getText();
			path.replaceAll("\\", "\\\\");
			return path;
		}
		return descriptionImportPathText.getText();
	}

	/**
	 * Helper method to Get the Evaluation's Import Path. Also takes care of the
	 * Path separator of Windows.
	 * 
	 * @return the String representation of the Assignment Evaluation Import
	 *         Path.
	 */
	protected final String getEvaluationImportPath() {
		if (isWindows()) {
			String path = evaluationImportPathText.getText();
			path.replaceAll("\\", "\\\\");
			return path;
		}

		return evaluationImportPathText.getText();
	}

	// Method to handle the Browse Buttons
	private void handleButtons(SelectionEvent event) {

		if (event.widget == descriptionBrowse)
			handleDescriptionBrowse();

		if (event.widget == evaluationBrowse)
			handleEvaluationBrowse();
	}

	// Opens the FileDialog and sets the descriptionImportPathText according to
	// the File Selection
	private void handleDescriptionBrowse() {
		FileDialog dialog = new FileDialog(getShell(), SWT.OPEN);
		dialog.setFilterExtensions(new String[] { "*.asg" });
		dialog.setFilterNames(new String[] { "Assignment Files (*.asg)" });
		String path = dialog.open();
		descriptionImportPathText.setText(path);
	}

	// Opens the FileDialog and sets the evaluationImportPathText according to
	// the File Selection
	private void handleEvaluationBrowse() {
		FileDialog dialog = new FileDialog(getShell(), SWT.OPEN);
		dialog.setFilterExtensions(new String[] { "*.asg" });
		dialog.setFilterNames(new String[] { "Assignment Files (*.asg)" });
		String path = dialog.open();
		evaluationImportPathText.setText(path);
	}

	// Opens a ExceptionDialog showing the Exception Message
	private void handleException(Exception e) {
		IStatus status = new Status(3, "AssignmentDescriptionException", -1,
				"An Exception has Occured !", e);
		ErrorDialog error = new ErrorDialog(getShell(), "Error",
				e.getMessage(), status, 0);
		error.create();
		error.open();
	}

	/**
	 * Checks if the Current OS is Windows.
	 * 
	 * @return true if the current OS is Windows.
	 */
	public boolean isWindows() {
		String os = System.getProperty("os.name").toLowerCase();
		return (os.indexOf("win") >= 0);
	}

	/**
	 * Opens the Assignment Editor Dialog for Editing.
	 * 
	 * @param e
	 *            the SelectionEvent instance
	 */
	protected void openEditorDialog(SelectionEvent e) {

		EditorDialog descriptionDialog;
		EditorDialog evaluationDialog;

		if (e.widget == editDesc) {
			descriptionDialog = new EditorDialog(getShell(), descriptionBuffer);
			descriptionDialog.open();
			descriptionBuffer.initEdit(descriptionTextField, getShell()
					.getDisplay());
			descriptionTextField.layout();
		}

		if (e.widget == editEval) {
			evaluationDialog = new EditorDialog(getShell(), evaluationBuffer);
			evaluationDialog.open();
			evaluationBuffer.initEdit(evaluationTextField, getShell()
					.getDisplay());
			evaluationTextField.layout();
		}
	}

	// Reads the contents of the file selected to be imported and hence sets the
	// descriptionBuffer
	private void readDescriptionFromFile(String path) {
		File file = new File(path);

		if (!file.exists()) {
			updateStatus("File Does Not Exist");
			return;
		}
		if (file.isDirectory()) {
			updateStatus("Specified Path is a Directory");
			return;
		}
		try {
			byte fileBuffer[] = new byte[(int) file.length() + 1];
			FileInputStream contentStream = new FileInputStream(file);
			contentStream.read(fileBuffer);
			descriptionBuffer.setBuffer(new String(fileBuffer));
		} catch (Exception e) {
			handleException(e);
		}

		updateStatus(null);
	}

	// Reads the contents of the file selected to be imported and hence sets the
	// evaluationBuffer
	private void readEvaluationFromFile(String path) {
		File file = new File(path);
		if (!file.exists()) {
			updateStatus("File Does Not Exist");
			return;
		}

		if (file.isDirectory()) {
			updateStatus("Specified Path is a Directory");
			return;
		}
		try {
			setMessage("File Exists and can be Imported");
			byte fileBuffer[] = new byte[(int) file.length() + 1];
			FileInputStream contentStream = new FileInputStream(file);
			contentStream.read(fileBuffer);
			evaluationBuffer.setBuffer(new String(fileBuffer));
		} catch (Exception e) {
			handleException(e);
		}

		updateStatus(null);
	}

	//updates Page status
	private void updateStatus(String message) {
		setErrorMessage(message);
		setPageComplete(message == null);
	}

}
